#pragma once

#include <memory>

#include <boost/asio.hpp>
#include <boost/array.hpp>

#include "ServerImp.h"

namespace frame {
namespace imp {
class UniqueNumber;
class ITimerTask;
} //end of namespace imp
} //end of namespace  frame

namespace frame {
namespace imp {
namespace asio {


class AsioWriteBuffer;
class AsioServerChannelImp : public ServerChannelImp, public std::enable_shared_from_this<AsioServerChannelImp>
{
public:
    static const int kReadBufferSize = 1024*8;

    AsioServerChannelImp(const AsioServerChannelImp &) = delete;
    AsioServerChannelImp & operator=(const AsioServerChannelImp &) = delete;
    virtual ~AsioServerChannelImp();

    typedef std::shared_ptr<AsioServerChannelImp> pointer;
    static pointer newInstance(ServerImp * serverImp); //factory interface for create AsioServerImp;


    boost::asio::ip::tcp::socket & getSocket() {
        return socket;
    }

    std::string getRemoteIp() override;
    const int getRemotePort() override;

    bool open() override;
    bool close() override;


    void write(const char * buf, int size) override; 
    void disconnect() override;

protected:
    AsioServerChannelImp(uint64_t id, ServerImp * serverImp);
    
    void startRead();
    void handleRead(const boost::system::error_code & error, size_t bytes_transferred);
    void startWrite(std::shared_ptr<AsioWriteBuffer> writeBufferOnce);
    void handleWrited(const boost::system::error_code & error, size_t bytes_transferred);
    
private:
    std::shared_ptr<AsioWriteBuffer> writeBuffer; //TODO: Thread-safe to write buffer
    boost::array<char, kReadBufferSize> data; //read buffer 
    boost::asio::ip::tcp::socket socket;

    std::mutex channelMutex;
};


class AsioTimerTasks;
class AsioServerImp : public ServerImp{
public:
    AsioServerImp(const int port);
    virtual ~AsioServerImp();

    AsioServerImp(const AsioServerImp &) = delete;
    AsioServerImp & operator=(const AsioServerImp &) = delete;


    int start() override;
    int run() override;
    void stop() override;

    uint64_t addTimerTask(ITimerTask * task, uint64_t timespan) override;
    void removeTimerTask(uint64_t timerId) override;

    boost::asio::io_context& getIoContext(){
        return ioContext;
    }

protected:
    void startAccept();
    void handleAccept(AsioServerChannelImp::pointer newServerChannelImp, const boost::system::error_code& error);

    void startTimer();
private:
    boost::asio::io_context ioContext;
    boost::asio::ip::tcp::acceptor acceptor;

    UniqueNumber *timerNumber;
    AsioTimerTasks *timerTasks;
};

} //end of namespace asio
} //end of namespace imp
} //end of namespace  frame
